package com.baidu.iit.pvm.process;

import com.baidu.iit.pvm.ActivityException;
import com.baidu.iit.pvm.PvmProcessDefinition;
import com.baidu.iit.pvm.PvmProcessInstance;
import com.baidu.iit.pvm.runtime.ExecutionImpl;
import com.baidu.iit.pvm.runtime.InterpretableExecution;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 流程定义实现类
 * 由该类实现流程的节点定义
 * Created by 卫立 on 2014/4/7.
 */
public class ProcessDefinitionImpl extends ScopeImpl implements PvmProcessDefinition {

    protected String name; //流程名称
    protected String key;  //流程的键值
    protected String description; //流程描述功能描述
    protected ActivityImpl initial; //开始节点，该节点有且必须有一个

    private final ReentrantLock lock = new ReentrantLock();

    /**
     * 流程节点定义，实现节点的快速定位，用map模拟堆栈的时候，主要是为了首节点是个单元的情况
     */
    protected Map<ActivityImpl, List<ActivityImpl>> initialActivityStacks = new HashMap<ActivityImpl, List<ActivityImpl>>();


    /**
     * 构造函数，传入流程的ID
     *
     * @param id
     */
    public ProcessDefinitionImpl(String id) {
        super(id, null);
        processDefinition = this;
    }


    /**
     * 根据流程定义创建流程执行实例
     *
     * @return 返回流程的执行体
     */
    public PvmProcessInstance createProcessInstance() {

        if (initial == null) {
            throw new ActivityException("Process '" + name + "' 没有定义开始节点,在流程定义中必须要有开始节点!");
        }
        return createProcessInstanceForInitial(initial);
    }


    /**
     * 根据流程的起始节点，创建流程实例
     *
     * @param initial
     * @return
     */
    public PvmProcessInstance createProcessInstanceForInitial(ActivityImpl initial) {

        if (initial == null) {
            throw new ActivityException("Process '" + name + "' 没有定义开始节点,在流程定义中必须要有开始节点!");
        }


        //流程实例创建点
        InterpretableExecution processInstance = newProcessInstance(initial);

        processInstance.setProcessDefinition(this);

        //自关联设置，先有鸡还是先有蛋呢？
        processInstance.setProcessInstance(processInstance);

        processInstance.initialize();


        InterpretableExecution scopeInstance = processInstance;

        //判断起始点是否是子流程，流程单元的处理
        List<ActivityImpl> initialActivityStack = getInitialActivityStack(initial);

        //判断节点中的某些节点是不是块属性的
        for (ActivityImpl initialActivity : initialActivityStack) {
            if (initialActivity.isScope()) {
                scopeInstance = (InterpretableExecution) scopeInstance.createExecution();
                scopeInstance.setActivity(initialActivity);
                scopeInstance.initialize();
            }
        }

       //设置流程的起始点
       scopeInstance.setActivity(initial);

        return processInstance;
    }

    /**
     * 获取起始流程的定义的堆栈，相对与scope的状况，既当起始的流程是SCOPE中的某个节点时，获取他的父节点作为起点
     *
     * @return
     */
    public List<ActivityImpl> getInitialActivityStack() {
        return getInitialActivityStack(initial);
    }


    /**
     * 初始花初始节点的堆栈信息，由于可能存在多个线程同时读取的情况所以要进行加锁操作
     * @param startActivity
     * @return
     */
    public List<ActivityImpl> getInitialActivityStack(ActivityImpl startActivity) {
        lock.lock();
        try {
            List<ActivityImpl> initialActivityStack = initialActivityStacks.get(startActivity);

            if (initialActivityStack == null) {

                initialActivityStack = new ArrayList<ActivityImpl>();

                ActivityImpl activity = startActivity;

                while (activity != null) {
                    initialActivityStack.add(0, activity);
                    activity = activity.getParentActivity();
                }

                initialActivityStacks.put(startActivity, initialActivityStack);
            }
            return initialActivityStack;

        } finally {
            lock.unlock();
        }

    }

    /**
     * 创建流程的执行实例
     *
     * @param startActivity
     * @return
     */
    protected InterpretableExecution newProcessInstance(ActivityImpl startActivity) {
        return new ExecutionImpl(startActivity);
    }


    /**
     * 获取初始的节点信息
     *
     * @return
     */
    public ActivityImpl getInitial() {
        return initial;
    }

    /**
     * 设置起始节点
     * @param initial
     */
    public void setInitial(ActivityImpl initial) {
        this.initial = initial;
    }


    public String toString() {
        return "ProcessDefinition(" + id + ")";
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getDescription() {
        return this.description;
    }

}